package com.gitee.feizns.webtemplate.commons.exception;

import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.gitee.feizns.webtemplate.commons.exception.http.HttpException;
import com.gitee.feizns.webtemplate.commons.exception.service.ServiceException;
import com.gitee.feizns.webtemplate.entity.vo.commons.result.Result;
import org.apache.tomcat.util.http.fileupload.FileUploadBase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;

/**
 * @author feizns
 * @since 2019/3/13
 */
@RestControllerAdvice
@EnableConfigurationProperties(ExceptionProperties.class)
public class ExceptionRestControllerAdvice {

    /**
     * 日志
     */
    private static final Logger logger = LoggerFactory.getLogger(ExceptionRestControllerAdvice.class);

    @Autowired
    private ExceptionProperties properties;

    /**
     * 业务异常
     * @param e
     * @return
     */
    @ExceptionHandler(ServiceException.class)
    public Result<?> serviceException(ServiceException e) {
        return defaultResult(e.getMessage(), e.getStatus(), e);
    }

    /**
     * 数据校验
     * @param e
     * @return
     */
    @ExceptionHandler(BindException.class)
    public ResponseEntity<Result<?>> bindException(BindException e) {
        return new ResponseEntity<Result<?>>(
                defaultResult(e.getBindingResult().getFieldError().getDefaultMessage(), e),
                properties.getValidateStatus());
    }

    /**
     * 数据校验，注解：{@Valid}
     * @param e
     * @return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<Result<?>> method(MethodArgumentNotValidException e) {
        return new ResponseEntity<Result<?>>(
                defaultResult(e.getBindingResult().getFieldError().getDefaultMessage(), e),
                properties.getValidateStatus());
    }

    /**
     * 数据解析
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({ InvalidFormatException.class, HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class })
    public Result<?> bindException(Exception e) {
        return defaultResult("数据解析错误. 原因：" + e.getMessage(), e);
    }

    /**
     * 自定义响应码的异常
     * @param e
     * @return
     */
    @ExceptionHandler(HttpException.class)
    public ResponseEntity<Result<?>> httpException(HttpException e) {
        return new ResponseEntity<Result<?>>(defaultResult(e.getMessage(), e), e.getStatus());
    }

    /**
     * 没有拦截的异常
     * @param e
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseEntity<Result<?>> allException(Exception e) {
        return new ResponseEntity<Result<?>>(defaultResult(e), properties.getUnknownStatus());
    }

    //---------------------------------------------------------------------
    // 文件上传异常
    //---------------------------------------------------------------------

    /**
     * 获取文件上传大小
     */
    @Value("${spring.servlet.multipart.max-file-size}")
    private String maxFileSize;

    @ExceptionHandler(MultipartException.class)
    public Result<?> multipartException(MultipartException e) {
        Throwable tmp = e.getCause();

        while ( tmp != null ) {
            if ( tmp.getClass() == FileUploadBase.FileSizeLimitExceededException.class )
                return defaultResult("文件大于" + maxFileSize + ".", e);
            tmp = tmp.getCause();
        }

        return defaultResult("文件上传失败.", e);
    }

    //---------------------------------------------------------------------
    // 定制响应
    //---------------------------------------------------------------------

    private Result defaultResult(Throwable e) {
        return defaultResult(e.getMessage(), e);
    }

    private Result defaultResult(String msg, Throwable e) {
        return defaultResult(msg, null, e);
    }

    private Result defaultResult( String msg, Object data, Throwable e) {
        if ( properties.isDebug() )
            e.printStackTrace();

        if ( logger.isWarnEnabled() )
            logger.warn(e.toString());

        return data != null ? Result.error(msg, data) : Result.error(msg);
    }

}
